#include "channel.h"
#include <math.h>
#include <memory.h>
#include <stdio.h>

void
init_channels(Channels* cl)
{
  memset(cl, 0, sizeof(Channels));
}

dir_t
channel_dir_changed(Channels* cl)
{
  dir_t d = 0;

  if (cl->cl[1].slp_dn == cl->cl[1].slp_up && cl->cl[1].type == 1 &&
      cl->cl[0].slp_dn == cl->cl[0].slp_up &&
      cl->cl[0].slp_up == cl->cl[1].slp_up)
    d = cl->cl[1].slp_dn;
  else if (cl->cl[0].slp_dn == cl->cl[0].slp_up &&
           cl->cl[1].slp_dn == cl->cl[1].slp_up &&
           cl->cl[2].slp_dn == cl->cl[2].slp_up &&
           cl->cl[0].slp_up == cl->cl[1].slp_up &&
           cl->cl[2].slp_up == cl->cl[1].slp_up)
    d = cl->cl[1].slp_dn;
  else if (cl->cl[2].slp_dn == cl->cl[2].slp_up && cl->cl[2].type == 1 &&
           cl->cl[3].slp_dn == cl->cl[3].slp_up &&
           cl->cl[3].slp_dn == cl->cl[2].slp_dn && cl->cl[0].type != 1 &&
           cl->cl[1].type != 1) {
    d = cl->cl[2].slp_dn;
  }

  if (d != cl->cur_dir) {
    cl->cur_dir = d;
    return d;
  } else {
    return (0);
  }
}

void
print_channel(Channels* cl)
{
  printf("CLD %d: ", cl->cur_dir);
  for (int8_t i = 0; i < 7; ++i) {
    printf("%d %d %d; ", cl->cl[i].slp_up, cl->cl[i].slp_dn, cl->cl[i].type);
  }
  printf("\n");
}

#ifndef THE_BIG_NEEDLE
#define THE_BIG_NEEDLE 20
#endif // THE_BIG_NEEDLE

void
get_max_min_in_mma(const MaxMinArray mma, price_t* hp_max, price_t* hp_min)
{
  *hp_max = -1e20;
  *hp_min = 1e20;
  for (int i = 0; i < MAX_MA_DIR; i++) {
    if (mma[i].hp_max > *hp_max)
      *hp_max = mma[i].hp_max;
    if (mma[i].hp_min < *hp_min)
      *hp_min = mma[i].hp_min;
  }
}

void
get_dirs_in_mma(const MaxMinArray mma,
                MaDirArray mda,
                IsBigNeedle bn,
                const price_t hp_now,
                const time_t tm_now)
{
  for (int i = 0; i < MAX_MA_DIR; i++) {
    bn[i] = 0;
    if (mma[i].tm_max > mma[i].tm_min) {
      mda[i] = (fabs(hp_now - mma[i].hp_max) > 5) ? (-1) : 1;
      // dir_t b = sign(delta);
      if ((fabs(mma[i].hp_max - hp_now) > THE_BIG_NEEDLE))
        bn[i] = 1;
    } else if (mma[i].tm_max < mma[i].tm_min) {
      mda[i] = (fabs(hp_now - mma[i].hp_min) > 5) ? (1) : -1;
      if ((fabs(mma[i].hp_min - hp_now) > THE_BIG_NEEDLE))
        bn[i] = -1;
    } else {
      mda[i] = 0;
    }
  }
}

static dir_t
get_dir_bigneedle_(const IsBigNeedle bn)
{
  dir_t d = 0;
  for (int8_t i = 0; i < MAX_MA_DIR; ++i) {
    if (!d) {
      if (bn[i])
        d = bn[i];
    } else {
      if (bn[i] && d != bn[i]) {
        d = 0;
        break;
      }
    }
  }
  return d;
}

static Boolean
channel_one_same_dir_same_(const Channels* cl, const level_t l, const dir_t d)
{
  const ChannelLines* cls = &cl->cl[l];
  return (cls->slp_dn == d || cls->slp_up == d) &&
         (!(cls->slp_dn == d && cls->slp_up == d));
}

static Boolean
channel_both_same_dir_same_(const Channels* cl, const level_t l, const dir_t d)
{
  const ChannelLines* cls = &cl->cl[l];
  return (cls->slp_dn == d && cls->slp_up == d);
}

static Boolean
channel_both_anti_dir_same_(const Channels* cl, const level_t l, const dir_t d)
{
  const ChannelLines* cls = &cl->cl[l];
  return (cls->slp_dn == -d && cls->slp_up == -d);
}

dir_t
get_channel_dir(const Channels* cl, const MaDirArray mda, const IsBigNeedle bn)
{
  dir_t d_bn = get_dir_bigneedle_(bn);
  dir_t c = 0;

  if (mda[0] == mda[1]) {
    if (mda[1] == mda[2]) {
      // ALL MMD are same
      const dir_t d = mda[0];
      if (channel_both_same_dir_same_(cl, 2, d)) {
        if (channel_one_same_dir_same_(cl, 0, d) &&
            channel_one_same_dir_same_(cl, 1, d)) {
          if (channel_both_anti_dir_same_(cl, 3, d)) {
            c = 0;
          } else {
            c = d;
          }
        } else if (channel_both_same_dir_same_(cl, 0, d) &&
                   channel_both_same_dir_same_(cl, 1, d)) {
          c = d;
        }
      } else if (channel_one_same_dir_same_(cl, 2, d)) {
        if (channel_one_same_dir_same_(cl, 0, d) &&
            channel_one_same_dir_same_(cl, 1, d) &&
            channel_one_same_dir_same_(cl, 3, d)) {
          c = d;
        } else if (channel_both_same_dir_same_(cl, 0, d) &&
                   channel_both_same_dir_same_(cl, 1, d)) {
          c = d;
        }
      } else if (channel_one_same_dir_same_(cl, 0, d) &&
                 channel_one_same_dir_same_(cl, 1, d) &&
                 channel_one_same_dir_same_(cl, 3, d)) {
        c = d;
      } else if (channel_both_anti_dir_same_(cl, 1, d) &&
                 channel_both_anti_dir_same_(cl, 2, d)) {
        if (channel_both_same_dir_same_(cl, 0, d) &&
            (channel_one_same_dir_same_(cl, 3, d) ||
             channel_one_same_dir_same_(cl, 4, d))) {
          if (!d_bn) {
            c = 0;
          } else {
            c = -d;
          }
        } else if (channel_one_same_dir_same_(cl, 3, d)) {
          if (!d_bn) {
            c = -d;
          } else {
            c = d;
          }
        } else if (channel_both_anti_dir_same_(cl, 0, d) &&
                   channel_both_anti_dir_same_(cl, 3, d)) {
          if (!d_bn) {
            c = -d;
          } else {
            c = d;
          }
        } else if (channel_one_same_dir_same_(cl, 0, d)) {
          if (!d_bn) {
            c = -d;
          } else {
            c = d;
          }
        }
      } else if (channel_both_anti_dir_same_(cl, 2, d) &&
                 channel_both_same_dir_same_(cl, 0, d) &&
                 channel_both_same_dir_same_(cl, 1, d)) {
        if (!d_bn) {
          c = -d;
        } else {
          c = d;
        }
      }
    } else {
      const dir_t d = mda[0];
      if (d_bn) {
        c = d;
      } else {
        c = 0;
      }
    }
  }

  return c;
}

dir_t
pre_opposite(const Channels* cl, const MaDirArray mda, const dir_t cd)
{
  if (mda[0] == mda[1] && mda[1] == mda[2]) {
    const dir_t d = mda[0];
    if (d == -cd) {
      if (channel_both_same_dir_same_(cl, 0, d) &&
          channel_both_same_dir_same_(cl, 1, d)) {
        return d;
      } else if (channel_both_same_dir_same_(cl, 0, d) &&
                 channel_one_same_dir_same_(cl, 1, d)) {
        return d;
      } else if (channel_one_same_dir_same_(cl, 0, d) &&
                 channel_both_same_dir_same_(cl, 1, d)) {
        return d;
      }
    }
  }

  if (mda[0] == mda[1] && mda[1] == mda[2] && mda[2] == mda[3] && mda[0] == -cd)
    return (0);

  return cd;
}
